<?php
/**
 * Abstract class for handling external HTML links.
 * @package halo
 */

hc_core_ClassLoader::load('hc_uri_Uri');

/**
 * Abstract class for handling external HTML links.
 *
 * Base class that creates abstracts externally included links. Examples would be
 * <script /> for Javascript and <link /> for Stylesheets. See the subclasses
 * for implementation details.
 * @package halo
 */
abstract class halo_AbstractExternalLinkViewHelper {

    /**
     * @var halo_HttpRequest
     */
    private $httpRequest;

    /**
     * @var halo_HttpResponse
     */
    private $httpResponse;

    /**
     * @var hc_uri_Uri
     */
    private $hcUri;

    /**
     * URIs to be included.
     * @var array
     */
    private $uris = array();

    /**
     * Options for each individual URI.
     * @var array
     */
    private $uriOptions = array();

    /**
     * Cache of URIs that were included inline.
     * @var array
     */
    private $inlinedUris = array();

    /**
     * Constructor.
     *
     * The Abstract External Link View Helper is request specific, so it requires
     * that a valid HTTP Request and HTTP Response be passed to its constructor.
     * @param halo_HttpRequest $httpRequest The request.
     * @param halo_HttpResponse $httpResponse The response.
     */
    public function __construct(halo_HttpRequest $httpRequest, halo_HttpResponse $httpResponse) {
        $this->httpRequest = $httpRequest;
        $this->httpResponse = $httpResponse;
        $context = halo_DispatcherUtil::GET_CONTEXT($this->httpRequest);
        $uriConfiguration = $context->get('uriConfiguration');
        $this->hcUri = new hc_uri_Uri($uriConfiguration, $httpRequest->getScriptPathRoot());
    }

    /**
     * Include the external link inline.
     *
     * Includes the external link "here" instead of pushing it to the <head />.
     *
     * An exaple using the JavascriptViewHelper.
     *
     * <code>
     * // Would include the following HERE (not in <head />)
     * // <script type="text/javascript" src="/js/sampl.js"></script>
     * print $linkViewHelper->inline('/js/sample.js');
     * </code>
     *
     * @param string $path External link's path.
     * @param array $options Options.
     * @return string Rendered link.
     */
    final public function inline($path, $options = null) {
        list($uri, $options) = $this->normalizeInput($path, $options);
        $allow = true;
        if ( in_array($uri, $this->inlinedUris) ) {
            $allow = (isset($options['allowDuplicates']) and $options['allowDuplicates']) ?
                true : false;
        } else {
            $this->inlinedUris[] = $uri;
        }
        return $allow ? $this->renderExternalLink(htmlspecialchars($uri), $options) : '';
    }

    /**
     * Include the external link in <head />.
     *
     * Includes the external link and stores it for use later. This is useful
     * if particular views require an arbitrary amount of Javascript or stylesheets
     * be loaded. By inlcuding them in this way, all included links will be added
     * to the <head /> in a shell.
     *
     * An exaple using the JavascriptViewHelper.
     *
     * <code>
     * // Would include the following in <head />
     * // <script type="text/javascript" src="/js/sampl.js"></script>
     * $linkViewHelper->inc('/js/sample.js');
     * </code>
     *
     * @param string $path External link's path.
     * @param array $options Options.
     */
    final public function inc($path, $options = null) {
        list($uri, $options) = $this->normalizeInput($path, $options);
        if ( ! in_array($uri, $this->uris) ) {
            $this->uris[] = $uri;
            $this->uriOptions[$uri] = $options;
        }
    }

    /**
     * Normalize the path and options input.
     *
     * Takes the specified path and options and tweaks them as appropriate.
     * @param string $path External link's path.
     * @param array $options Options.
     * @return array Normalized URI and options. ($uri, $options)
     */
    private function normalizeInput($path, $options = null) {
        if ( $options === null ) $options = array();
        if ( ! is_array($options) ) $options = array('site' => $options);
        if ( isset($options['site']) ) {
            $uri = $this->hcUri->getSite($options['site'], $path);
        } else {
            $uri = $this->hcUri->get($path);
        }
        return array($uri, $options);
    }
    
    /**
     * Renders included links.
     *
     * This is useful in the <head /> of a view. Any links included using {@link inc()}
     * will be included here.
     *
     * <code>
     * <head>
     *     <title>Sample output</title>
     *     <!-- Render all <script></script> we have included elsewhere in the template. -->
     *     <?php print $javascriptViewHelper->renderIncluded(); ?>
     * </head>
     * </code>
     * @return string
     */
    final public function renderIncluded() {
        $buffer = '';
        foreach ( $this->uris as $uri ) {
            if ( ! in_array($uri, $this->inlinedUris) ) {
                $buffer .= $this->renderExternalLink(htmlspecialchars($uri), $this->uriOptions[$uri]);
            }
        }
        return $buffer;
    }

    /**
     * Render an external link.
     *
     * Subclasses should implement this method as it is called for generating
     * the actual link text that will be returned by {@link renderIncluded()} and
     * {@link inline()}.
     *
     * @param string $uri Sanitized URI
     * @param array $options Options.
     * @return string Rendered link.
     */
    abstract protected function renderExternalLink($uri, $options);
}
?>
